 /*******************************************************************************
  * Copyright (c) 2003, 2006 IBM Corporation and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
  * http://www.eclipse.org/legal/epl-v10.html
  *
  * Contributors:
  * IBM Corporation - initial API and implementation
  *******************************************************************************/
 package org.eclipse.ui.navigator;

 import java.util.Collections ;
 import java.util.Comparator ;
 import java.util.HashMap ;
 import java.util.Iterator ;
 import java.util.Map ;
 import java.util.SortedSet ;
 import java.util.TreeMap ;
 import java.util.TreeSet ;

 import org.eclipse.core.runtime.Assert;
 import org.eclipse.jface.action.IAction;
 import org.eclipse.jface.action.IMenuManager;
 import org.eclipse.jface.action.Separator;
 import org.eclipse.jface.viewers.ISelection;
 import org.eclipse.jface.viewers.IStructuredSelection;
 import org.eclipse.ui.IWorkbench;
 import org.eclipse.ui.IWorkbenchWindow;
 import org.eclipse.ui.PlatformUI;
 import org.eclipse.ui.actions.ActionContext;
 import org.eclipse.ui.actions.ActionGroup;
 import org.eclipse.ui.internal.navigator.wizards.CommonWizardDescriptor;
 import org.eclipse.ui.internal.navigator.wizards.CommonWizardDescriptorManager;
 import org.eclipse.ui.internal.navigator.wizards.WizardShortcutAction;
 import org.eclipse.ui.wizards.IWizardDescriptor;
 import org.eclipse.ui.wizards.IWizardRegistry;

 /**
  *
  * Populates context menus with shortcut actions for defined wizards. Wizards
  * may be defined by any of the following extension points:
  * <p>
  * <ul>
  * <li><b>org.eclipse.ui.newWizards</b></li>
  * <li><b>org.eclipse.ui.importWizards</b></li>
  * <li><b>org.eclipse.ui.exportWizards</b></li>
  * </ul>
  * </p>
  * <p>
  * Here are the required steps for using this feature correctly:
  * <ol>
  * <li>Declare all new/import/export wizards from the extension points above,
  * or locate the existing wizards that you intend to reuse.</li>
  * <li>Declare <b>org.eclipse.ui.navigator.navigatorContent/commonWizard</b>
  * elements to identify which wizards should be associated with what items in
  * your viewer or navigator.</li>
  * <li>If you are using Resources in your viewer and have bound the resource
  * extension declared in <b>org.eclipse.ui.navigator.resources</b>, then you
  * will get most of this functionality for free.</li>
  * <li>Otherwise, you may choose to build your own custom menu. In which case,
  * you may instantiate this class, and hand it the menu or submenu that you want
  * to list out the available wizard shortcuts via
  * {@link WizardActionGroup#fillContextMenu(IMenuManager)}.</li>
  * </ol>
  * </p>
  * <p>
  * Clients may instantiate, but not subclass WizardActionGroup.
  * </p>
  *
  * @see PlatformUI#getWorkbench()
  * @see IWorkbench#getNewWizardRegistry()
  * @see IWorkbench#getImportWizardRegistry()
  * @see IWorkbench#getExportWizardRegistry()
  * @since 3.2
  *
  */
 public final class WizardActionGroup extends ActionGroup {

     /**
      * The type for commonWizard extensions with the value "new" for their type
      * attribute.
      */
     public static final String TYPE_NEW = "new"; //$NON-NLS-1$

     /**
      * The type for commonWizard extensions with the value "new" for their type
      * attribute.
      */
     public static final String TYPE_IMPORT = "import"; //$NON-NLS-1$

     /**
      * The type for commonWizard extensions with the value "new" for their type
      * attribute.
      */
     public static final String TYPE_EXPORT = "export"; //$NON-NLS-1$

     private static final CommonWizardDescriptor[] NO_DESCRIPTORS = new CommonWizardDescriptor[0];
     
     private static final String [] NO_IDS = new String [0];
     
     private CommonWizardDescriptor[] descriptors;

     /* a map of (id, IAction)-pairs. */
     private Map actions;

     /*
      * the window is passed to created WizardShortcutActions for the shell and
      * selection service.
      */
     private IWorkbenchWindow window;

     /* the correct wizard registry for this action group (getRegistry()) */
     private IWizardRegistry wizardRegistry;

     private boolean disposed = false;

     private String type;

     private INavigatorContentService contentService;

     /**
      *
      * @param aWindow
      * The window that will be used to acquire a Shell and a
      * Selection Service
      * @param aWizardRegistry
      * The wizard registry will be used to locate the correct wizard
      * descriptions.
      * @param aType
      * Indicates the value of the type attribute of the commonWizard
      * extension point. Use any of the TYPE_XXX constants defined on
      * this class.
      * @see PlatformUI#getWorkbench()
      * @see IWorkbench#getNewWizardRegistry()
      * @see IWorkbench#getImportWizardRegistry()
      * @see IWorkbench#getExportWizardRegistry()
      */
     public WizardActionGroup(IWorkbenchWindow aWindow,
             IWizardRegistry aWizardRegistry, String aType) {
         super();
         Assert.isNotNull(aWindow);
         Assert.isNotNull(aWizardRegistry);
         Assert
                 .isTrue(aType != null
                         && (TYPE_NEW.equals(aType) || TYPE_IMPORT.equals(aType) || TYPE_EXPORT
                                 .equals(aType)));
         window = aWindow;
         wizardRegistry = aWizardRegistry;
         type = aType;

     }
     

     /**
      *
      * @param aWindow
      * The window that will be used to acquire a Shell and a
      * Selection Service
      * @param aWizardRegistry
      * The wizard registry will be used to locate the correct wizard
      * descriptions.
      * @param aType
      * Indicates the value of the type attribute of the commonWizard
      * extension point. Use any of the TYPE_XXX constants defined on
      * this class.
      * @param aContentService
      * The content service to use when deciding visibility.
      * @see PlatformUI#getWorkbench()
      * @see IWorkbench#getNewWizardRegistry()
      * @see IWorkbench#getImportWizardRegistry()
      * @see IWorkbench#getExportWizardRegistry()
      */
     public WizardActionGroup(IWorkbenchWindow aWindow,
             IWizardRegistry aWizardRegistry, String aType, INavigatorContentService aContentService) {
         this(aWindow, aWizardRegistry, aType);
         contentService = aContentService;

     }

     public void setContext(ActionContext aContext) {
         Assert.isTrue(!disposed);

         super.setContext(aContext);
         if (aContext != null) {
             ISelection selection = aContext.getSelection();
             Object element = null;
             if (selection instanceof IStructuredSelection) {
                 element = ((IStructuredSelection) selection).getFirstElement();
             }
             if(element == null) {
                 element = Collections.EMPTY_LIST;
             }
             // null should be okay here
 setWizardActionDescriptors(CommonWizardDescriptorManager.getInstance()
                     .getEnabledCommonWizardDescriptors(element, type, contentService));
         } else {
             setWizardActionDescriptors(NO_DESCRIPTORS);
         }
     }

     /*
      * (non-Javadoc)
      *
      * @see org.eclipse.ui.actions.ActionGroup#fillContextMenu(org.eclipse.jface.action.IMenuManager)
      */
     public void fillContextMenu(IMenuManager menu) {
         Assert.isTrue(!disposed);
  
         if (descriptors != null) {
             Map groups = findGroups();
             SortedSet sortedWizards = null;
             String menuGroupId = null;
             for (Iterator menuGroupItr = groups.keySet().iterator(); menuGroupItr.hasNext();) {
                 menuGroupId = (String ) menuGroupItr.next();
                 sortedWizards = (SortedSet ) groups.get(menuGroupId);
                 menu.add(new Separator(menuGroupId));
                 for (Iterator wizardItr = sortedWizards.iterator(); wizardItr.hasNext();) {
                     menu.add((IAction) wizardItr.next());
                 }
             }
         }
     }

     /**
      * @return A Map of menuGroupIds to SortedSets of IActions.
      */
     private synchronized Map /*<String, SortedSet<IAction>>*/ findGroups() {
         IAction action = null;
         Map groups = new TreeMap ();
         SortedSet sortedWizards = null;
         String menuGroupId = null;
         for (int i = 0; i < descriptors.length; i++) {
             menuGroupId = descriptors[i].getMenuGroupId() != null ?
                             descriptors[i].getMenuGroupId() : CommonWizardDescriptor.DEFAULT_MENU_GROUP_ID;
             sortedWizards = (SortedSet ) groups.get(menuGroupId);
             if(sortedWizards == null) {
                 groups.put(descriptors[i].getMenuGroupId(), sortedWizards = new TreeSet (ActionComparator.INSTANCE));
             }
             if ((action = getAction(descriptors[i].getWizardId())) != null) {
                 sortedWizards.add(action);
             }
         }
         return groups;
     }


     public void dispose() {
         super.dispose();
         actions = null;
         window = null;
         descriptors = null;
         wizardRegistry = null;
         disposed = true;
     }

     /*
      * (non-Javadoc) Returns the action for the given wizard id, or null if not
      * found.
      */
     protected IAction getAction(String id) {
         if (id == null || id.length() == 0) {
             return null;
         }

         // Keep a cache, rather than creating a new action each time,
 // so that image caching in ActionContributionItem works.
 IAction action = (IAction) getActions().get(id);
         if (action == null) {
             IWizardDescriptor descriptor = wizardRegistry.findWizard(id);
             if (descriptor != null) {
                 action = new WizardShortcutAction(window, descriptor);
                 getActions().put(id, action);
             }
         }

         return action;
     }

     /**
      * @return a map of (id, IAction)-pairs.
      */
     protected Map getActions() {
         if (actions == null) {
             actions = new HashMap ();
         }
         return actions;
     }

     /**
      * @return Returns the wizardActionIds.
      */
     public synchronized String [] getWizardActionIds() {
         if(descriptors != null && descriptors.length > 0) {
             String [] wizardActionIds = new String [descriptors.length];
             for (int i = 0; i < descriptors.length; i++) {
                 wizardActionIds[i] = descriptors[i].getWizardId();
             }
             return wizardActionIds;
         }
         return NO_IDS;
     }

     /**
      * @param theWizardDescriptors
      * The wizard action ids to set. These should be defined through
      * <b>org.eclipse.ui.xxxWizards</b>
      */
     protected synchronized void setWizardActionDescriptors(CommonWizardDescriptor[] theWizardDescriptors) {
         descriptors = theWizardDescriptors;
     }
       
     private static class ActionComparator implements Comparator {
         
         private static final ActionComparator INSTANCE = new ActionComparator();
         /* (non-Javadoc)
          * @see java.util.Comparator#compare(java.lang.Object, java.lang.Object)
          */
         public int compare(Object arg0, Object arg1) {
             return ((IAction)arg0).getText().compareTo(((IAction)arg1).getText());
         }
     }
 }

